/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import { sax } from './sax.js';
import { bom } from './bom.js';
import { EventEmitter as events } from './events.js';
import { defaults } from './defaults.js';
// import { every } from './timers.js';

let parser = {}

var isEmpty, processItem, processors, setImmediate;
let bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; };
let hasProp = {}.hasOwnProperty;
let extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }

isEmpty = function (thing) {
return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
};

processItem = function (processors, item, key) {
var i, len, process;
for (i = 0, len = processors.length; i < len; i++) {
  process = processors[i];
  item = process(item, key);
}
return item;
};

parser.Parser = (function (superClass) {
  extend(Parser, superClass);

  function Parser(opts) {
    this.parseStringPromise = bind(this.parseStringPromise, this);
    this.parseString = bind(this.parseString, this);
    this.reset = bind(this.reset, this);
    this.assignOrPush = bind(this.assignOrPush, this);
    this.processAsync = bind(this.processAsync, this);
    var key, ref, value;
    if (!(this instanceof Parser)) {
      return new Parser(opts);
    }
    this.options = {};
    ref = defaults["0.2"];
    for (key in ref) {
      if (!hasProp.call(ref, key)) continue;
      value = ref[key];
      this.options[key] = value;
    }
    for (key in opts) {
      if (!hasProp.call(opts, key)) continue;
      value = opts[key];
      this.options[key] = value;
    }
    if (this.options.xmlns) {
      this.options.xmlnskey = this.options.attrkey + "ns";
    }
    if (this.options.normalizeTags) {
      if (!this.options.tagNameProcessors) {
        this.options.tagNameProcessors = [];
      }
      this.options.tagNameProcessors.unshift(processors.normalize);
    }
    this.reset();
  }

  Parser.prototype.processAsync = function () {
    var chunk, err;
    try {
      if (this.remaining.length <= this.options.chunkSize) {
        chunk = this.remaining;
        this.remaining = '';
        this.saxParser = this.saxParser.write(chunk);
        return this.saxParser.close();
      } else {
        chunk = this.remaining.substr(0, this.options.chunkSize);
        this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length);
        this.saxParser = this.saxParser.write(chunk);
        return setImmediate(this.processAsync);
      }
    } catch (error1) {
      err = error1;
      if (!this.saxParser.errThrown) {
        this.saxParser.errThrown = true;
        return this.emit(err);
      }
    }
  };

  Parser.prototype.assignOrPush = function (obj, key, newValue) {
    if (!(key in obj)) {
      if (!this.options.explicitArray) {
        return obj[key] = newValue;
      } else {
        return obj[key] = [newValue];
      }
    } else {
      if (!(obj[key] instanceof Array)) {
        obj[key] = [obj[key]];
      }
      return obj[key].push(newValue);
    }
  };

  Parser.prototype.reset = function () {
    var attrkey, charkey, ontext, stack;
    this.removeAllListeners();
    this.saxParser = sax.parser(this.options.strict, {
      trim: false,
      normalize: false,
      xmlns: this.options.xmlns
    });
    this.saxParser.errThrown = false;
    this.saxParser.onerror = (function (_this) {
      return function (error) {
        _this.saxParser.resume();
        if (!_this.saxParser.errThrown) {
          _this.saxParser.errThrown = true;
          return _this.emit("error", error);
        }
      };
    })(this);
    this.saxParser.onend = (function (_this) {
      return function () {
        if (!_this.saxParser.ended) {
          _this.saxParser.ended = true;
          return _this.emit("end", _this.resultObject);
        }
      };
    })(this);
    this.saxParser.ended = false;
    this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
    this.resultObject = null;
    stack = [];
    attrkey = this.options.attrkey;
    charkey = this.options.charkey;
    this.saxParser.onopentag = (function (_this) {
      return function (node) {
        var key, newValue, obj, processedKey, ref;
        obj = {};
        obj[charkey] = "";
        if (!_this.options.ignoreAttrs) {
          ref = node.attributes;
          for (key in ref) {
            if (!hasProp.call(ref, key)) continue;
            if (!(attrkey in obj) && !_this.options.mergeAttrs) {
              obj[attrkey] = {};
            }
            newValue = _this.options.attrValueProcessors ? processItem(_this.options.attrValueProcessors, node.attributes[key], key) : node.attributes[key];
            processedKey = _this.options.attrNameProcessors ? processItem(_this.options.attrNameProcessors, key) : key;
            if (_this.options.mergeAttrs) {
              _this.assignOrPush(obj, processedKey, newValue);
            } else {
              obj[attrkey][processedKey] = newValue;
            }
          }
        }
        obj["#name"] = _this.options.tagNameProcessors ? processItem(_this.options.tagNameProcessors, node.name) : node.name;
        if (_this.options.xmlns) {
          obj[_this.options.xmlnskey] = {
            uri: node.uri,
            local: node.local
          };
        }
        return stack.push(obj);
      };
    })(this);
    this.saxParser.onclosetag = (function (_this) {
      return function () {
        var cdata, emptyStr, key, node, nodeName, obj, objClone, old, s, xpath;
        obj = stack.pop();
        nodeName = obj["#name"];
        if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) {
          delete obj["#name"];
        }
        if (obj.cdata === true) {
          cdata = obj.cdata;
          delete obj.cdata;
        }
        s = stack[stack.length - 1];
        if (obj[charkey].match(/^\s*$/) && !cdata) {
          emptyStr = obj[charkey];
          delete obj[charkey];
        } else {
          if (_this.options.trim) {
            obj[charkey] = obj[charkey].trim();
          }
          if (_this.options.normalize) {
            obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
          }
          obj[charkey] = _this.options.valueProcessors ? processItem(_this.options.valueProcessors, obj[charkey], nodeName) : obj[charkey];
          if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
            obj = obj[charkey];
          }
        }
        if (isEmpty(obj)) {
          obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr;
        }
        if (_this.options.validator != null) {
          xpath = "/" + ((function () {
            var i, len, results;
            results = [];
            for (i = 0, len = stack.length; i < len; i++) {
              node = stack[i];
              results.push(node["#name"]);
            }
            return results;
          })()).concat(nodeName).join("/");
          (function () {
            var err;
            try {
              return obj = _this.options.validator(xpath, s && s[nodeName], obj);
            } catch (error1) {
              err = error1;
              return _this.emit("error", err);
            }
          })();
        }
        if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') {
          if (!_this.options.preserveChildrenOrder) {
            node = {};
            if (_this.options.attrkey in obj) {
              node[_this.options.attrkey] = obj[_this.options.attrkey];
              delete obj[_this.options.attrkey];
            }
            if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
              node[_this.options.charkey] = obj[_this.options.charkey];
              delete obj[_this.options.charkey];
            }
            if (Object.getOwnPropertyNames(obj).length > 0) {
              node[_this.options.childkey] = obj;
            }
            obj = node;
          } else if (s) {
            s[_this.options.childkey] = s[_this.options.childkey] || [];
            objClone = {};
            for (key in obj) {
              if (!hasProp.call(obj, key)) continue;
              objClone[key] = obj[key];
            }
            s[_this.options.childkey].push(objClone);
            delete obj["#name"];
            if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
              obj = obj[charkey];
            }
          }
        }
        if (stack.length > 0) {
          return _this.assignOrPush(s, nodeName, obj);
        } else {
          if (_this.options.explicitRoot) {
            old = obj;
            obj = {};
            obj[nodeName] = old;
          }
          _this.resultObject = obj;
          _this.saxParser.ended = true;
          return _this.emit("end", _this.resultObject);
        }
      };
    })(this);
    ontext = (function (_this) {
      return function (text) {
        var charChild, s;
        s = stack[stack.length - 1];
        if (s) {
          s[charkey] += text;
          if (_this.options.explicitChildren && _this.options.preserveChildrenOrder && _this.options.charsAsChildren && (_this.options.includeWhiteChars || text.replace(/\\n/g, '').trim() !== '')) {
            s[_this.options.childkey] = s[_this.options.childkey] || [];
            charChild = {
              '#name': '__text__'
            };
            charChild[charkey] = text;
            if (_this.options.normalize) {
              charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim();
            }
            s[_this.options.childkey].push(charChild);
          }
          return s;
        }
      };
    })(this);
    this.saxParser.ontext = ontext;
    return this.saxParser.oncdata = (function (_this) {
      return function (text) {
        var s;
        s = ontext(text);
        if (s) {
          return s.cdata = true;
        }
      };
    })(this);
  };

  Parser.prototype.parseString = function (str, cb) {
    var err;
    if ((cb != null) && typeof cb === "function") {
      this.on("end", function (result) {
        this.reset();
        return cb(null, result);
      });
      this.on("error", function (err) {
        this.reset();
        return cb(err);
      });
    }
    try {
      str = str.toString();
      if (str.trim() === '') {
        this.emit("end", null);
        return true;
      }
      str = bom.stripBOM(str);
      if (this.options.async) {
        this.remaining = str;
        setImmediate(this.processAsync);
        return this.saxParser;
      }
      return this.saxParser.write(str).close();
    } catch (error1) {
      err = error1;
      if (!(this.saxParser.errThrown || this.saxParser.ended)) {
        this.emit('error', err);
        return this.saxParser.errThrown = true;
      } else if (this.saxParser.ended) {
        throw err;
      }
    }
  };

  Parser.prototype.parseStringPromise = function (str) {
    return new Promise((function (_this) {
      return function (resolve, reject) {
        return _this.parseString(str, function (err, value) {
          if (err) {
            return reject(err);
          } else {
            return resolve(value);
          }
        });
      };
    })(this));
  };

  return Parser;

})(events);

parser.parseString = function (str, a, b) {
  var cb, options, parser1;
  if (b != null) {
    if (typeof b === 'function') {
      cb = b;
    }
    if (typeof a === 'object') {
      options = a;
    }
  } else {
    if (typeof a === 'function') {
      cb = a;
    }
    options = {};
  }
  parser1 = new parser.Parser(options);
  return parser1.parseString(str, cb);
};

parser.parseStringPromise = function (str, a) {
  var options, parser1;
  if (typeof a === 'object') {
    options = a;
  }
  parser1 = new parser.Parser(options);
  return parser1.parseStringPromise(str);
};

export { parser }